#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QDateTime>
#include <QMessageBox>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("Server"); //设置窗口标题
    mServer = new QTcpServer; //实例化一个 QTcpServer 对象
    enumIpAddress(); //获取当前设备所有ip地址

    //处理信号与槽
    //处理新连接客户端
    connect(mServer, SIGNAL(newConnection()),this, SLOT(newConnectionSlot()));
}

Widget::~Widget()
{
    delete ui;
}

//枚举(获取)设备所有可用的ip地址
//然后筛选出符合特定条件(ipv4)的ip地址
//并将这些代码添加到界面上的一个下拉列表框，方便后续选择
void Widget::enumIpAddress()
{
    //allAddresses() 函数返回所有网络接口所关联的ip地址
    //每个 QHostAddress 对象代表一个 IP 地址
    QList<QHostAddress> ipList = QNetworkInterface::allAddresses();
    QStringList addressStrList;//创建一个字符串列表对象 存储后续筛选出来的字符串形式的ip地址
    addressStrList.clear();//将该列表初始化清空

    for(int index = 0;index<ipList.size();index++)//遍历获取到的所有可用有效ip地址
    {
        if(ipList.at(index).isNull())   continue;     //如果地址为空,则去掉
        //枚举类型:区分网络协议类型                           //获取该ip地址所属的网络层协议类型
        QAbstractSocket::NetworkLayerProtocol protocol = ipList.at(index).protocol();
        if(protocol!= QAbstractSocket::IPv4Protocol)   continue;   //只取IPV4的地址
        addressStrList.append(ipList.at(index).toString());//将ip地址转化字符串形式再加进ip地址列表(字符串)
    }
    ui->comboBox_Address->addItems(addressStrList);//将 addressStrList中存储的所有筛选后的 IP 地址字符串添加到下拉列表框中
}

//监听功能函数
void Widget::on_pushButton_listen_clicked()
{
    //currentText函数获取当前选中的文本内容，也就是用户在界面上选择的服务器 IP 地址
    QString serverAddressStr = ui->comboBox_Address->currentText();     //获取服务器ip地址
    //调用其text函数获取输入框中的文本内容，再使用 toInt 函数将该文本转换为整数类型
    quint16 port = ui->lineEdit_port->text().toInt();//获取服务器端口
    QHostAddress serverAddress = QHostAddress(serverAddressStr); //初始化协议族(ip地址)

    if(mServer->isListening())
    {
        //在监听状态 取消监听 不再接收新客户端的连接请求
        mServer->close();
        ui->pushButton_listen->setText("监听");//setText 函数将按钮上显示的文本修改为监听
    }
    else
    {
        //不在监听状态 开始监听
        if(mServer->listen(serverAddress, port))
        {
            //监听成功
            qDebug() << "Listen Ok!!";
            ui->pushButton_listen->setText("停止监听");
        }
        else
        {
            //监听失败 弹出警告对话框 提示错误原因
            QMessageBox::warning(this, "Tcp Server Listen Error", mServer->errorString());
        }
    }
}

//新链接处理函数
//服务器接收到新客户端连接请求并建立连接后，会触发对应的信号来调用这个槽函数
void Widget::newConnectionSlot()
{
    qDebug()<<"new connect signal";
    QString clientInfo;//存储新连接客户端的相关信息(ip地址和端口号等)

    mClient = mServer->nextPendingConnection();//返回一个代表新连接客户端的QTcpSocket类型的对象指针
    mClientList.append(mClient);//存入客户端连接表
    //获取Client 信息
    //通过新客户对象，调用peerAddress()获取其ip地址，调用tostring转换为字符串形式
    //然后和获取它的端口号转换为整数类型一起形成新客户的信息
    clientInfo = mClient->peerAddress().toString() + ":"+  QString::number(mClient->peerPort());

    ui->listWidget_client->insertItem(0, "");//先在好友列表插入一个空项
    ui->listWidget_client->addItem(clientInfo);//ddItem 函数将刚才组装好的包含客户端连接信息的clientInfo字符串添加进去

    //处理信号与槽
    connect(mClient, SIGNAL(readyRead()),this, SLOT(readyReadSlot()));          //接收消息
    connect(mClient, SIGNAL(disconnected()),this, SLOT(disconnectedSlot()));    //断开连接
}

//槽函数:触发接收消息信号
void Widget::readyReadSlot()
{
    QByteArray recvArray;
    QTcpSocket* current = nullptr;
    if (!mClientList.isEmpty())
    {
        // 接收客户端数据
        for (int index = 0; index < mClientList.count(); index++)
        {
            current = mClientList.at(index);

            if (current->isReadable())
            {
                recvArray = current->readAll();
                if (recvArray.isEmpty()) continue;

                // 获取发送方信息
                QString senderInfo = current->peerAddress().toString() + ":" + QString::number(current->peerPort());

                // 在服务器显示消息
                QString str = QString(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss ddd")) +
                              " [" + senderInfo + "]" + ":Recv\n" + QString::fromLocal8Bit(recvArray.data());
                ui->textBrowser_recv->append(str);
                break;
            }
        }

        // 转发给其他客户端，添加发送方信息
        for (int index = 0; index < mClientList.count(); index++)
        {
            QTcpSocket* temp = mClientList.at(index);
            if (current == temp) continue;
            if (temp->isWritable())
            {
                // 构造新的消息格式：发送方信息|原始消息
                QString senderInfo = current->peerAddress().toString() + ":" + QString::number(current->peerPort());
                QByteArray newMessage = (senderInfo + "|" + QString::fromLocal8Bit(recvArray.data())).toLocal8Bit();
                temp->write(newMessage);
            }
        }
    }
}

// 槽函数:断开连接
void Widget::disconnectedSlot()
{
    QTcpSocket* client = qobject_cast<QTcpSocket*>(sender());
    if (client)
    {
        QString clientInfo = client->peerAddress().toString() + ":" + QString::number(client->peerPort());
        QString str = QString(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss ddd")) +
                      " [" + clientInfo + "]" + " client close..";
        ui->textBrowser_recv->append(str);

        // 从好友列表中移除断开连接的客户端信息
        for (int i = 0; i < ui->listWidget_client->count(); ++i)
        {
            if (ui->listWidget_client->item(i)->text() == clientInfo)
            {
                QListWidgetItem* item = ui->listWidget_client->takeItem(i);
                delete item;
                break;
            }
        }

        // 从客户端列表中移除断开的客户端
        mClientList.removeOne(client);
        client->deleteLater();

        // 检查是否所有客户端都已断开连接，如果是，则弹出警告框
        if (mClientList.isEmpty())
        {
            QMessageBox::warning(this, "无客户端连接", "无客户可以收到你的消息");
        }
    }
}

// 槽函数: 发送功能
void Widget::on_pushButton_send_clicked()
{
    if (mClientList.isEmpty())
    {
        QMessageBox::warning(this, "无客户端连接", "无客户可以收到你的消息");
        return;
    }

    QString sendString = ui->plainTextEdit_send->toPlainText();
    QString serverInfo = mServer->serverAddress().toString() + ":" + QString::number(mServer->serverPort());

    // 构造带有服务器信息的消息
    QByteArray sendArr = (serverInfo + "|" + sendString).toLocal8Bit();

    for (int index = 0; index < mClientList.count(); index++)
    {
        QTcpSocket* temp = mClientList.at(index);
        if (temp->isWritable())
        {
            temp->write(sendArr);
        }
    }

    QString str = QString(QDateTime::currentDateTime().toString("yyyy.MM.dd hh:mm:ss ddd")) +
                  " [" + serverInfo + "]" + ":Send\n" + sendString;
    ui->textBrowser_recv->append(str);

    ui->plainTextEdit_send->clear();
}
